#pragma once

#include "DerivedNodeProp.h"
#include "JsiSkPoint.h"

#include <memory>
#include <string>
#include <utility>
#include <vector>

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdocumentation"

#include "include/core/SkPoint.h"

#pragma clang diagnostic pop

namespace RNSkia {
class PointModeProp : public DerivedProp<SkCanvas::PointMode> {
public:
    explicit PointModeProp(PropId name, const std::function<void(BaseNodeProp *)> &onChange)
        : DerivedProp<SkCanvas::PointMode>(onChange)
    {
        _pointModeProp = defineProperty<NodeProp>(name);
    }

    void updateDerivedValue() override
    {
        if (_pointModeProp->isSet()) {
            setDerivedValue(getPointModeFromStringValue(_pointModeProp->value().getAsString()));
        } else {
            setDerivedValue(nullptr);
        }
    }

private:
    static SkCanvas::PointMode getPointModeFromStringValue(const std::string &value)
    {
        if (value == "points") {
            return SkCanvas::PointMode::kPoints_PointMode;
        } else if (value == "lines") {
            return SkCanvas::PointMode::kLines_PointMode;
        } else if (value == "polygon") {
            return SkCanvas::PointMode::kPolygon_PointMode;
        }
        throw std::runtime_error("Expected valid point mode for Points node, got \"" + value + "\".");
    }

    NodeProp *_pointModeProp;
};

class PointsProp : public DerivedProp<std::vector<SkPoint>> {
public:
    explicit PointsProp(PropId name, const std::function<void(BaseNodeProp *)> &onChange)
        : DerivedProp<std::vector<SkPoint>>(onChange)
    {
        _pointsProp = defineProperty<NodeProp>(name);
    }

    void updateDerivedValue() override
    {
        if (_pointsProp->isSet()) {
            auto pointsArray = _pointsProp->value().getAsArray();
            std::vector<SkPoint> points;
            points.reserve(pointsArray.size());
            for (size_t i = 0; i < pointsArray.size(); ++i) {
                auto p = pointsArray[i];
                points.push_back(PointProp::processValue(p));
            }
            setDerivedValue(std::move(points));
        } else {
            setDerivedValue(nullptr);
        }
    }

private:
    NodeProp *_pointsProp;
};
} // namespace RNSkia
